package com.doubao.wechat.service.impl;

import com.doubao.common.result.Result;
import com.doubao.common.utils.RedisTemplateFactory;
import com.doubao.wechat.config.OAuthConfig;
import com.doubao.wechat.dto.response.OAuthTokenResponse;
import com.doubao.wechat.dto.response.OAuthUserInfo;
import com.doubao.wechat.service.OAuthService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * OAuth 服务实现
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OAuthServiceImpl implements OAuthService {
    private final OAuthConfig oauthConfig;
    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;
    private RedisTemplate<String, Object> redisTemplate;

    @PostConstruct
    public void init() {
        this.redisTemplate = RedisTemplateFactory.getRedisTemplate();
    }

    @Override
    public String generateAuthorizationUrl() {
        return UriComponentsBuilder.fromHttpUrl(oauthConfig.getAuthorizationServerUrl())
                .queryParam("client_id", oauthConfig.getClientId())
                .queryParam("redirect_uri", oauthConfig.getRedirectUri())
                .queryParam("response_type", "code")
                .queryParam("scope", String.join(" ", oauthConfig.getScopes()))
                .queryParam("state", generateState())
                .build().toUriString();
    }

    @Override
    public Result<OAuthTokenResponse> getAccessToken(String code) {
        try {
            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
            params.add("client_id", oauthConfig.getClientId());
            params.add("client_secret", oauthConfig.getClientSecret());
            params.add("code", code);
            params.add("grant_type", "authorization_code");
            params.add("redirect_uri", oauthConfig.getRedirectUri());

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

            HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);

            ResponseEntity<OAuthTokenResponse> response = restTemplate.postForEntity(
                    oauthConfig.getTokenUrl(),
                    request,
                    OAuthTokenResponse.class
            );

            OAuthTokenResponse tokenResponse = response.getBody();
            if (tokenResponse != null) {
                cacheToken(tokenResponse);
                return Result.success(tokenResponse);
            }

            return Result.failed("获取令牌失败");
        } catch (Exception e) {
            log.error("获取访问令牌异常", e);
            return Result.failed("获取访问令牌异常：" + e.getMessage());
        }
    }

    @Override
    public Result<OAuthUserInfo> getUserInfo(String accessToken) {
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setBearerAuth(accessToken);

            HttpEntity<?> request = new HttpEntity<>(headers);

            ResponseEntity<OAuthUserInfo> response = restTemplate.exchange(
                    oauthConfig.getUserInfoUrl(),
                    HttpMethod.GET,
                    request,
                    OAuthUserInfo.class
            );

            OAuthUserInfo userInfo = response.getBody();
            if (userInfo != null) {
                return Result.success(userInfo);
            }

            return Result.failed("获取用户信息失败");
        } catch (Exception e) {
            log.error("获取用户信息异常", e);
            return Result.failed("获取用户信息异常：" + e.getMessage());
        }
    }

    @Override
    public Result<OAuthTokenResponse> refreshToken(String refreshToken) {
        try {
            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
            params.add("client_id", oauthConfig.getClientId());
            params.add("client_secret", oauthConfig.getClientSecret());
            params.add("refresh_token", refreshToken);
            params.add("grant_type", "refresh_token");

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

            HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);

            ResponseEntity<OAuthTokenResponse> response = restTemplate.postForEntity(
                    oauthConfig.getTokenUrl(),
                    request,
                    OAuthTokenResponse.class
            );

            OAuthTokenResponse tokenResponse = response.getBody();
            if (tokenResponse != null) {
                cacheToken(tokenResponse);
                return Result.success(tokenResponse);
            }

            return Result.failed("刷新令牌失败");
        } catch (Exception e) {
            log.error("刷新令牌异常", e);
            return Result.failed("刷新令牌异常：" + e.getMessage());
        }
    }

    /**
     * 缓存令牌
     *
     * @param tokenResponse 令牌响应
     */
    private void cacheToken(OAuthTokenResponse tokenResponse) {
        // 缓存访问令牌
        redisTemplate.opsForValue().set(
                "oauth:access_token:" + tokenResponse.getAccessToken(),
                tokenResponse,
                tokenResponse.getExpiresIn(),
                TimeUnit.SECONDS
        );

        // 缓存刷新令牌
        redisTemplate.opsForValue().set(
                "oauth:refresh_token:" + tokenResponse.getRefreshToken(),
                tokenResponse,
                7 * 24 * 60 * 60, // 7天
                TimeUnit.SECONDS
        );
    }

    /**
     * 生成防 CSRF 的 state 参数
     *
     * @return state 字符串
     */
    private String generateState() {
        String state = UUID.randomUUID().toString();
        // 可以将 state 存入 Redis，用于验证
        redisTemplate.opsForValue().set(
                "oauth:state:" + state,
                state,
                10 * 60, // 10分钟有效期
                TimeUnit.SECONDS
        );
        return state;
    }
}